package com.chukun.search.services.impl;

import com.chukun.search.base.HouseSort;
import com.chukun.search.base.HouseStatus;
import com.chukun.search.base.ServiceMultiResult;
import com.chukun.search.base.ServiceResult;
import com.chukun.search.dto.HouseDTO;
import com.chukun.search.dto.HouseDetailDTO;
import com.chukun.search.dto.HousePictureDTO;
import com.chukun.search.form.*;
import com.chukun.search.model.*;
import com.chukun.search.repository.*;
import com.chukun.search.services.IHouseService;
import com.chukun.search.services.IQiniuFileUploadService;
import com.chukun.search.services.ISearchService;
import com.chukun.search.utils.LoginUserUtils;
import com.google.common.collect.Maps;
import com.qiniu.common.QiniuException;
import com.qiniu.http.Response;
import org.modelmapper.ModelMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import javax.persistence.criteria.Predicate;
import java.util.*;
import java.util.stream.Collectors;

/**
 * Created by chukun.
 */
@Service
public class HouseServiceImpl implements IHouseService {

    @Autowired
    private SubwayRepository subwayRepository;

    @Autowired
    private SubwayStationRepository subwayStationRepository;
    @Autowired
    private ModelMapper modelMapper;

    @Autowired
    private HouseRepository houseRepository;
    @Autowired
    private HouseDetailRepository houseDetailRepository;
    @Autowired
    private HousePictureRepository housePictureRepository;
    @Autowired
    private HouseTagRepository houseTagRepository;
    @Autowired
    private IQiniuFileUploadService qiniuFileUploadService;
    @Autowired
    private ISearchService searchService;

    @Value("${qiniu.cdn.prefix}")
    private String cdnPrefix;

    /**
     * 保存房屋的基本信息
     * @param houseForm
     * @return
     */
    @Override
    public ServiceResult<HouseDTO> save(HouseForm houseForm) {
      //参数校验
        HouseDetail detail = new HouseDetail();
        ServiceResult<HouseDTO> houseDTOServiceResult = wrapperDetailInfo(detail,houseForm);
        if(houseDTOServiceResult!=null){
            return houseDTOServiceResult;
        }
        //将houseForm的基本参数封装到house
        House house = new House();
        modelMapper.map(houseForm,house);
        Date now = new Date();
        house.setCreateTime(now);
        house.setLastUpdateTime(now);
        house.setAdminId(LoginUserUtils.getLoginUserId());
        house = houseRepository.save(house);
        final Long houseId = house.getId();

        //封装detail信息
        detail.setHouseId(house.getId());
        detail = houseDetailRepository.save(detail);

        List<HousePicture> pictures = generateHousePictures(houseForm,house.getId());
        Iterable<HousePicture> housePictures = housePictureRepository.saveAll(pictures);

        HouseDTO houseDTO = modelMapper.map(house,HouseDTO.class);
        HouseDetailDTO houseDetailDTO = modelMapper.map(detail, HouseDetailDTO.class);
        houseDTO.setHouseDetail(houseDetailDTO);

        List<HousePictureDTO> housePictureDTOS = new ArrayList<>();
        pictures.forEach(housePicture -> housePictureDTOS.add(modelMapper.map(housePicture,HousePictureDTO.class)));
        houseDTO.setPictures(housePictureDTOS);
        houseDTO.setCover(this.cdnPrefix+houseDTO.getCover());

        if(!CollectionUtils.isEmpty(houseForm.getTags())){
            List<HouseTag> tags = houseForm.getTags().stream().map(p -> new HouseTag(houseId, p)).collect(Collectors.toList());
            houseTagRepository.saveAll(tags);
            houseDTO.setTags(houseForm.getTags());
        }
        return new ServiceResult<>(true,null,houseDTO);
    }

    /**
     * 浏览房源信息
     * @param searchBody
     * @return
     */
    @Override
    public ServiceMultiResult<HouseDTO> adminQuery(DatatableSearch searchBody) {
        List<HouseDTO> houseDTOS = new ArrayList<>();
        //封装查询条件
        Sort sort = new Sort(Sort.Direction.fromString(searchBody.getDirection()),searchBody.getOrderBy());
        int page = searchBody.getStart()/searchBody.getLength();

        Pageable pageable = PageRequest.of(page, searchBody.getLength(), sort);
        Specification specification = (root,query,cb)->{
            Predicate predicate = cb.equal(root.get("adminId"),LoginUserUtils.getLoginUserId());
             predicate = cb.notEqual(root.get("status"), HouseStatus.DELETED.getCode());

             if(!StringUtils.isEmpty(searchBody.getCity())){
                 predicate = cb.and(predicate,cb.equal(root.get("cityEnName"),searchBody.getCity()));
             }
             if(searchBody.getStatus()!=null){
                 predicate = cb.and(predicate,cb.equal(root.get("status"),searchBody.getStatus()));
             }
             if(searchBody.getCreateTimeMin()!=null){
                 predicate = cb.and(predicate,cb.greaterThanOrEqualTo(root.get("createTime"),searchBody.getCreateTimeMin()));
             }
             if(searchBody.getCreateTimeMax()!=null){
                 predicate = cb.and(predicate,cb.lessThanOrEqualTo(root.get("createTime"),searchBody.getCreateTimeMax()));
             }
             if(!StringUtils.isEmpty(searchBody.getTitle())){
                 predicate = cb.and(predicate,cb.like(root.get("title"), "%" + searchBody.getTitle() + "%"));
             }
             return predicate;
        };

        Page<House> houses= houseRepository.findAll(specification, pageable);

        houses.forEach(house->{
             HouseDTO houseDTO = modelMapper.map(house,HouseDTO.class);
             houseDTO.setCover(this.cdnPrefix+house.getCover());
             houseDTOS.add(houseDTO);
        });
        return new ServiceMultiResult<>(houses.getTotalElements(),houseDTOS);
    }

    /**
     * 根据house Id查询房源信息
     * @param id
     * @return
     */
    @Override
    public ServiceResult<HouseDTO> findHouseByHouseId(Long id) {
        Optional<House> houseOptional = houseRepository.findById(id);
        if(houseOptional==null || !houseOptional.isPresent() ){
            return ServiceResult.notFound();
        }
        House house = houseOptional.get();
        //将house装换为houseDto
        HouseDTO houseDTO = modelMapper.map(house,HouseDTO.class);

        //根据houseId,查询house详细
        HouseDetail houseDetail = houseDetailRepository.findByHouseId(id);
        if(houseDetail==null){
            return ServiceResult.notFound();
        }
        List<HousePicture> pictures = housePictureRepository.findAllByHouseId(id);
        HouseDetailDTO detailDTO = modelMapper.map(houseDetail,HouseDetailDTO.class);

        List<HousePictureDTO> housePictureDTOS = new ArrayList<>();
        for (HousePicture picture : pictures) {
            HousePictureDTO pictureDTO = modelMapper.map(picture, HousePictureDTO.class);
            housePictureDTOS.add(pictureDTO);
        }

        List<HouseTag> houseTags = houseTagRepository.findAllByHouseId(id);
        List<String> tags = new ArrayList<>();
        houseTags.forEach(tag->tags.add(tag.getName()));

        houseDTO.setHouseDetail(detailDTO);
        houseDTO.setPictures(housePictureDTOS);
        houseDTO.setTags(tags);

        return ServiceResult.of(houseDTO);
    }

    /**
     * 编辑接口实现
     * @param houseForm
     * @return
     */
    @Override
    public ServiceResult updateHouseInfo(HouseForm houseForm) {

        //检验房源是否存在
        Optional<House> houseOptional = houseRepository.findById(houseForm.getId());
        if(houseOptional==null || !houseOptional.isPresent()){
            return ServiceResult.notFound();
        }
        House house = houseOptional.get();
        //检查detail是否存在
        HouseDetail detail = houseDetailRepository.findByHouseId(houseForm.getId());
        if(detail==null){
            return  ServiceResult.notFound();
        }
        //填充基本字段
        ServiceResult<HouseDTO> serviceResult = wrapperDetailInfo(detail, houseForm);
        if(serviceResult!=null){
            return serviceResult;
        }

        List<HousePicture> pictures = generateHousePictures(houseForm, houseForm.getId());

        if (houseForm.getCover() == null) {
            houseForm.setCover(house.getCover());
        }
        modelMapper.map(houseForm,house);
        //保存house
        houseRepository.save(house);
        //保存detail
        houseDetailRepository.save(detail);
        //保存picture
        housePictureRepository.saveAll(pictures);

        //保存到es，只有状态为发布，才存入es
        if(house.getStatus()==HouseStatus.PASSES.getCode()){
            searchService.createHouseIndex(house.getId());
        }
        return ServiceResult.success();
    }

    /**
     * 移除图片接口
     * @param id
     * @return
     */
    @Override
    public ServiceResult removePhoto(Long id) {
        Optional<HousePicture> pictureOptional = housePictureRepository.findById(id);
        if (pictureOptional == null || !pictureOptional.isPresent()) {
            return ServiceResult.notFound();
        }
        HousePicture picture = pictureOptional.get();
        try {
            Response response = this.qiniuFileUploadService.deleteFileInQiniu(picture.getPath());
            if (response.isOK()) {
                housePictureRepository.deleteById(id);
                return ServiceResult.success();
            } else {
                return new ServiceResult(false, response.error);
            }
        } catch (QiniuException e) {
            e.printStackTrace();
            return new ServiceResult(false, e.getMessage());
        }
    }

    /**
     * 更新封面
     * @param coverId
     * @param targetId
     * @return
     */
    @Override
    @Transactional
    public ServiceResult updateCover(Long coverId, Long targetId) {
        Optional<HousePicture> coverOptional = housePictureRepository.findById(coverId);
        if (coverOptional == null || !coverOptional.isPresent()) {
            return ServiceResult.notFound();
        }
        HousePicture cover = coverOptional.get();
        houseRepository.updateCover(targetId, cover.getPath());
        return ServiceResult.success();
    }

    /**
     * 添加tag
     * @param houseId
     * @param tag
     * @return
     */
    @Override
    @Transactional
    public ServiceResult addTag(Long houseId, String tag) {
        Optional<House> houseOptional = houseRepository.findById(houseId);
        if (houseOptional == null || !houseOptional.isPresent()) {
            return ServiceResult.notFound();
        }

        HouseTag houseTag = houseTagRepository.findByNameAndHouseId(tag, houseId);
        if (houseTag != null) {
            return new ServiceResult(false, "标签已存在");
        }

        houseTagRepository.save(new HouseTag(houseId, tag));
        return ServiceResult.success();
    }

    /**
     * 删除tag
     * @param houseId
     * @param tag
     * @return
     */
    @Override
    public ServiceResult removeTag(Long houseId, String tag) {
        Optional<House> houseOptional = houseRepository.findById(houseId);
        if (houseOptional == null || !houseOptional.isPresent()) {
            return ServiceResult.notFound();
        }

        HouseTag houseTag = houseTagRepository.findByNameAndHouseId(tag, houseId);
        if (houseTag == null) {
            return new ServiceResult(false, "标签不存在");
        }
        houseTagRepository.deleteById(houseTag.getId());
        return ServiceResult.success();
    }

    @Override
    @Transactional
    public ServiceResult updateStatus(Long id, int status) {

        Optional<House> houseOptional = houseRepository.findById(id);
        if (houseOptional == null || !houseOptional.isPresent()) {
            return ServiceResult.notFound();
        }
         House house = houseOptional.get();
        if (house.getStatus() == status) {
            return new ServiceResult(false, "状态没有发生变化");
        }

        if (house.getStatus() == HouseStatus.RENTED.getCode()) {
            return new ServiceResult(false, "已出租的房源不允许修改状态");
        }

        if (house.getStatus() == HouseStatus.DELETED.getCode()) {
            return new ServiceResult(false, "已删除的资源不允许操作");
        }
        houseRepository.updateStatus(id, status);

        //更新状态为发布，存入es,其他状态删除es数据
        if(status==HouseStatus.PASSES.getCode()){
            searchService.createHouseIndex(house.getId());
        }else{
            searchService.removeHouseIndex(house.getId());
        }

        return ServiceResult.success();
    }

    /**
     * 查询房源信息
     * @param rentSearch
     * @return
     */
    @Override
    public ServiceMultiResult<HouseDTO> query(RentSearch rentSearch) {
        //搜索关键词不为空，查询es
         if(!StringUtils.isEmpty(rentSearch.getKeywords())){
             ServiceMultiResult<Long> serviceMultiResult = searchService.queryHouseIdWithEs(rentSearch);
             if(serviceMultiResult.getTotal()==0){
                 return new ServiceMultiResult<>(0, new ArrayList<>());
             }
             return new ServiceMultiResult<>(serviceMultiResult.getTotal(),wrapperHouseResult(serviceMultiResult.getResult()));
         }

        return queryDbHouseDatas(rentSearch);
    }

    /**
     * 地图整个查询
     * @param mapSearch
     * @return
     */
    @Override
    public ServiceMultiResult<HouseDTO> wholeMapQuery(MapSearch mapSearch) {
        ServiceMultiResult<Long> serviceResult = searchService.mapQuery(mapSearch.getCityEnName(), mapSearch.getOrderBy(), mapSearch.getOrderDirection(), mapSearch.getStart(), mapSearch.getSize());
        if (serviceResult.getTotal() == 0) {
            return new ServiceMultiResult<>(0, new ArrayList<>());
        }
        List<HouseDTO> houses = wrapperHouseResult(serviceResult.getResult());
        return new ServiceMultiResult<>(serviceResult.getTotal(), houses);
    }

    /**
     * 根据边界查询
     * @param mapSearch
     * @return
     */
    @Override
    public ServiceMultiResult<HouseDTO> boundMapQuery(MapSearch mapSearch) {
        ServiceMultiResult<Long> serviceResult = searchService.boundMapQuery(mapSearch);
        if (serviceResult.getTotal() == 0) {
            return new ServiceMultiResult<>(0, new ArrayList<>());
        }

        List<HouseDTO> houses = wrapperHouseResult(serviceResult.getResult());
        return new ServiceMultiResult<>(serviceResult.getTotal(), houses);
    }

    /**
     * 包装从es上查询的数据
     * @param houseIds
     * @return
     */
    private List<HouseDTO> wrapperHouseResult(List<Long> houseIds){
        List<HouseDTO> houseDTOS = new ArrayList<>();
        Map<Long,HouseDTO> mapList = Maps.newHashMap();
        Iterable<House> allHouseInfos = houseRepository.findAllById(houseIds);
        allHouseInfos.forEach(house -> {
            HouseDTO houseDTO = modelMapper.map(house,HouseDTO.class);
            houseDTO.setCover(this.cdnPrefix+house.getCover());
            mapList.put(house.getId(),houseDTO);
        });
        wrapperHouseList(houseIds,mapList);

        //矫正顺序
        for(Long houseId:houseIds){
            houseDTOS.add(mapList.get(houseId));
        }
        return houseDTOS;
    }

    /**
     * 从数据库里面查询对应的信息
     * @param rentSearch
     * @return
     */
    private ServiceMultiResult<HouseDTO> queryDbHouseDatas(RentSearch rentSearch){
        Sort sort = HouseSort.generateSort(rentSearch.getOrderBy(),rentSearch.getOrderDirection());
        int page = rentSearch.getStart()/rentSearch.getSize();
        Pageable pageable = PageRequest.of(page,rentSearch.getSize(),sort);
        Specification<House> houseSpecification = (root,query,cb)->{
            Predicate predicate = cb.equal(root.get("status"),HouseStatus.PASSES.getCode());
            predicate = cb.and(predicate,cb.equal(root.get("cityEnName"),rentSearch.getCityEnName()));
            return predicate;
        };
        Page<House> houses = houseRepository.findAll(houseSpecification, pageable);
        List<HouseDTO> houseDTOS = new ArrayList<>();
        Map<Long,HouseDTO> mapHouseDTOS = Maps.newHashMap();
        List<Long> houseIds = new ArrayList<>();
        houses.forEach(house -> {
            HouseDTO houseDTO = modelMapper.map(house, HouseDTO.class);
            houseDTO.setCover(this.cdnPrefix+house.getCover());
            houseDTOS.add(houseDTO);
            houseIds.add(house.getId());
            mapHouseDTOS.put(house.getId(),houseDTO);
        });
        //封装房源详细信息，tag标签
        wrapperHouseList(houseIds,mapHouseDTOS);
        return new ServiceMultiResult<>(houses.getTotalElements(),houseDTOS);
    }


    /**
     * 处理房源详细信息，tag信息
     * @param houseIds
     * @param mapHouseDTOS
     */
    private void wrapperHouseList(List<Long> houseIds, Map<Long, HouseDTO> mapHouseDTOS) {
        List<HouseDetail> houseDetails = houseDetailRepository.findAllByHouseIdIn(houseIds);
        houseDetails.forEach(detail->{
            HouseDTO houseDTO = mapHouseDTOS.get(detail.getHouseId());
            HouseDetailDTO houseDetailDTO = modelMapper.map(detail, HouseDetailDTO.class);
            houseDTO.setHouseDetail(houseDetailDTO);
        });
        List<HouseTag> houseTags = houseTagRepository.findAllByHouseIdIn(houseIds);
        houseTags.forEach(tag->{
            HouseDTO houseDTO = mapHouseDTOS.get(tag.getHouseId());
            houseDTO.getTags().add(tag.getName());
        });
    }

    /**
     * 图片对象列表信息填充
     * @param houseForm
     * @param id
     * @return
     */
    private List<HousePicture> generateHousePictures(HouseForm houseForm, Long houseId) {

        List<HousePicture> pictures = new ArrayList<>();
        if(CollectionUtils.isEmpty(pictures)){
            return pictures;
        }
        for (PhotoForm photo : houseForm.getPhotos()) {
            HousePicture picture = new HousePicture();
            picture.setHouseId(houseId);
            picture.setCdnPrefix(cdnPrefix);
            picture.setPath(photo.getPath());
            picture.setWidth(photo.getWidth());
            picture.setHeight(photo.getHeight());
            pictures.add(picture);
        }
        return pictures;
    }

    /**
     * 检验地跌线路是否正确，封装houseDetail的基本信息
     * @param detail
     * @param houseForm
     * @return
     */
    private ServiceResult<HouseDTO> wrapperDetailInfo(HouseDetail detail, HouseForm houseForm) {
        //检验地铁信息
        Optional<Subway> subwayOptional = subwayRepository.findById(houseForm.getSubwayLineId());
        if(subwayOptional==null || !subwayOptional.isPresent()){
            return new ServiceResult<>(false,"not valid subWay line");
        }
        Subway subway = subwayOptional.get();
        Optional<SubwayStation> subwayStationOptional = subwayStationRepository.findById(houseForm.getSubwayStationId());
        if(subwayStationOptional==null || !subwayStationOptional.isPresent()){
            return new ServiceResult<>(false,"not valid subWay station line");
        }
        SubwayStation subwayStation = subwayStationOptional.get();
        if(subwayStation.getSubwayId().longValue()!=subway.getId()){
            return new ServiceResult<>(false,"not valid subWay station line");
        }

        detail.setSubwayLineId(subway.getId());
        detail.setSubwayLineName(subway.getName());

        detail.setSubwayStationId(subwayStation.getId());
        detail.setSubwayStationName(subwayStation.getName());

        detail.setDescription(houseForm.getDescription());
        detail.setDetailAddress(houseForm.getDetailAddress());
        detail.setLayoutDesc(houseForm.getLayoutDesc());
        detail.setRentWay(houseForm.getRentWay());
        detail.setRoundService(houseForm.getRoundService());
        detail.setTraffic(houseForm.getTraffic());
        return null;
    }
}
